package Demo;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: DELL
 * Date: 2023-01-06
 * Time: 15:39
 */

/**
 * 懒汉模式的单例模式实现
 */
class Singletonlazy {


    //private static Singletonlazy instance = null;

    //public static Singletonlazy getInstance() { // 可以给方法加锁
    // 给读 比较 写 操作加锁成原子 保证不脏读
        /*synchronized (Singletonlazy.class) {
            if (instance == null) {
                instance = new Singletonlazy();
                // 这个实例并非类加载的时候就去创建了,而是真正第一次使用的时候才去创建(如果不用,就不创建了)
                // 因此为懒汉模式
                // 既有读又有写 多线程情况下不安全 -- 多线程时可能会触发多次new情况,导致不安全(不在为单例)
            }
        }*/

    /**
     * 上述操作中,加锁只是在new对象之前加上的,是非常有必要的, 但new完之后
     * 继续调用getInstance,此时instance的值一定是一个非空的值,会直接触发
     * return 相当于一个是 比较操作 一个是返回操作 -- 这两操作都是读操作
     * 多线程共同只读 不加锁无线程安全问题, 但是却在重复加锁, 增大了开销
     * 优化如下:
     */


        /*if (instance == null) {
            synchronized (Singletonlazy.class) {
                if (instance == null) {
                    instance = new Singletonlazy();
                }
            }
        }*/


    /**
     * 上述任然存在问题: 假设有很多线程都去进行getInstance操作时
     * 1. 内存可见性问题(编译器优化) 只有第一次真正读取了内存,后续都是读取
     * 寄存器或者cache
     *
     * 2. 指令重排序问题
     * instance = new Singleton(); new可以拆分成三个步骤
     * 1: 申请内存空间
     * 2: 调用构造方法,把这个内存空间初始化成一个合理的对象
     * 3: 把内存空间的地址赋值给instance引用
     * 正常情况下时按照 1 2 3 顺序执行
     * 当编译器指令重排序时, 1 2 3 可能变成了 1 3 2 (单线程时,123和132本质结果没有区别,都时创建一个实例)
     *
     * 当多线程时, t1 按照1 3 2 执行, t1 执行到 1 3后被切除cpu t2来执行
     * 当t1执行完3 后, 在t2 看来此处的引用就非空了,直接返回该引用,并且可能会尝试引用其中属性
     * 但是由于t1 的2还为执行完 t2拿到的就是一个非法的对象(还未完全构造完) 后续去用时会出现问题
     *
     * volatile关键字解决内存可见性和禁止指令重排序
     *
     */

    private volatile static Singletonlazy instance = null;

    public static Singletonlazy getInstance() {
        if (instance == null) {
            synchronized (Singletonlazy.class) {
                if (instance == null) {
                    instance = new Singletonlazy();
                }
            }
        }
        return instance;
    }

    private Singletonlazy() {
    }
}

public class Thread_Demo12 {
    public static void main(String[] args) {
        Singletonlazy s1 = Singletonlazy.getInstance(); // 第一次调用的时候instance 为判定条件为真 创建了一个实例给s1
        Singletonlazy s2 = Singletonlazy.getInstance(); // 第二次调用的时候, instance为s1 判定为假 直接返回instance 因此为s1
        System.out.println(s1 == s2);
    }
}
